#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <pthread.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "../common/seg.h"
#include "../utils/concurrent.h"
#include "stcp_client.h"


client_tcb_t* tcb_list[MAX_TRANSPORT_CONNECTIONS];
pthread_mutex_t mutex_list[MAX_TRANSPORT_CONNECTIONS];
pthread_cond_t cond_list[MAX_TRANSPORT_CONNECTIONS];


int connection;

/*面向应用层的接口*/

//
//  我们在下面提供了每个函数调用的原型定义和细节说明, 但这些只是指导性的, 你完全可以根据自己的想法来设计代码.
//
//  注意: 当实现这些函数时, 你需要考虑FSM中所有可能的状态, 这可以使用switch语句来实现.
//
//  目标: 你的任务就是设计并实现下面的函数原型.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// stcp客户端初始化
//
// 这个函数初始化TCB表, 将所有条目标记为NULL.  
// 它还针对重叠网络TCP套接字描述符conn初始化一个STCP层的全局变量, 该变量作为sip_sendseg和sip_recvseg的输入参数.
// 最后, 这个函数启动seghandler线程来处理进入的STCP段. 客户端只有一个seghandler.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//

void stcp_client_init(int conn) {
  connection=conn;
  for(int i=0;i<MAX_TRANSPORT_CONNECTIONS;i++){
    mutex_list[i]=(pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
    cond_list[i]=(pthread_cond_t)PTHREAD_COND_INITIALIZER;
    tcb_list[i]=NULL;
  }
  pthread_t handler_tid;
  pthread_create(&handler_tid,NULL,seghandler,NULL);
}

// 创建一个客户端TCB条目, 返回套接字描述符
//
// 这个函数查找客户端TCB表以找到第一个NULL条目, 然后使用malloc()为该条目创建一个新的TCB条目.
// 该TCB中的所有字段都被初始化. 例如, TCB state被设置为CLOSED，客户端端口被设置为函数调用参数client_port. 
// TCB表中条目的索引号应作为客户端的新套接字ID被这个函数返回, 它用于标识客户端的连接. 
// 如果TCB表中没有条目可用, 这个函数返回-1.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
int stcp_client_sock(unsigned int client_port) {
  int sockfd=0;
  for(;sockfd<MAX_TRANSPORT_CONNECTIONS;sockfd++){
    int successful_flag=0;
    pthread_mutex_t* mutex=&mutex_list[sockfd];
    pthread_mutex_lock(mutex);

    if(tcb_list[sockfd]==NULL){
      client_tcb_t* tcb=(client_tcb_t*)calloc(1,sizeof(client_tcb_t));
      tcb->client_portNum=client_port;
      tcb->state=CLOSED;
      tcb->next_seqNum=0;
      tcb->next_ackNum=0;
      tcb->sendBufHead=(segBuf_t*)calloc(1,sizeof(segBuf_t));
      tcb->sendBufunSent=tcb->sendBufHead;
      tcb->sendBufTail=NULL;
      tcb->unAck_segNum=0;
      tcb->wait_flag=0;
      tcb->sockfd=sockfd;
      tcb_list[sockfd]=tcb;
      successful_flag=1;
    }
    pthread_mutex_unlock(mutex);
    if(successful_flag)break;
  }
  if(sockfd==MAX_TRANSPORT_CONNECTIONS){
    return -1;
  }else {
    return sockfd;
  }
}

// 连接STCP服务器
//
// 这个函数用于连接服务器. 它以套接字ID和服务器的端口号作为输入参数. 套接字ID用于找到TCB条目.  
// 这个函数设置TCB的服务器端口号,  然后使用sip_sendseg()发送一个SYN段给服务器.  
// 在发送了SYN段之后, 一个定时器被启动. 如果在SYNSEG_TIMEOUT时间之内没有收到SYNACK, SYN 段将被重传. 
// 如果收到了, 就返回1. 否则, 如果重传SYN的次数大于SYN_MAX_RETRY, 就将state转换到CLOSED, 并返回-1.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//

//UNDONE
int stcp_client_connect(int sockfd, int server_nodeID, unsigned int server_port) {
  pthread_mutex_t* mutex=&mutex_list[sockfd];
  client_tcb_t* tcb=NULL;
  seg_t* syn_seg=NULL;

  pthread_mutex_lock(mutex);
  if((tcb=tcb_list[sockfd])!=NULL){
    tcb->server_portNum=server_port;
	  tcb->server_nodeID=server_nodeID;
    syn_seg=create_syn_seg(tcb->client_portNum,tcb->server_portNum);
    pthread_mutex_unlock(mutex);
  }else{
    pthread_mutex_unlock(mutex);
    return -1;
  }

  sip_sendseg(connection,server_nodeID,syn_seg);

  pthread_mutex_lock(mutex);
  if((tcb=tcb_list[sockfd])!=NULL){
    tcb->state=SYNSENT;
    pthread_mutex_unlock(mutex);
  }else{
    pthread_mutex_unlock(mutex);
    return -1;
  }
  
  int is_connected=0;
  for(int i=0;i<SYN_MAX_RETRY;i++){
    clock_t start,end;
    double duration=0;
    start=clock();

    while(duration<SYN_TIMEOUT){
      pthread_mutex_lock(mutex);
      if((tcb=tcb_list[sockfd])!=NULL){
        if(tcb->state==CONNECTED){
          is_connected=1;
        }
        pthread_mutex_unlock(mutex);
      }else{
        pthread_mutex_unlock(mutex);
        return -1;
      }
      if(is_connected)break;
      end=clock();
      duration = ((double)(end-start)/CLOCKS_PER_SEC) * 1e9;
    }

    if(is_connected)
      break;
    else{
        concurrent_printf_reminder("stcp_client_connect:retry syn_seg!\n");
        sip_sendseg(connection,server_nodeID,syn_seg);
      }
  }

  if(is_connected){
    pthread_t tid;
    u_int64_t arg=sockfd;
    pthread_create(&tid,NULL,sendBuf_timer,(void*)arg);
    concurrent_printf_reminder("stcp_client_connect successed!\n");
    return 1;
  }else {
    pthread_mutex_lock(mutex);
    if((tcb=tcb_list[sockfd])!=NULL){
      tcb->state=CLOSED;
    }
    pthread_mutex_unlock(mutex);
    concurrent_printf_warning("stcp_client_connect failed!timeout!\n");
    return -1;
  }
}

// 发送数据给STCP服务器. 这个函数使用套接字ID找到TCB表中的条目. 
// 然后它使用提供的数据创建segBuf, 将它附加到发送缓冲区链表中. 
// 如果发送缓冲区在插入数据之前为空, 一个名为sendbuf_timer的线程就会启动. 
// 每隔SENDBUF_ROLLING_INTERVAL时间查询发送缓冲区以检查是否有超时事件发生.
// 这个函数在成功时返回1，否则返回-1. 
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//

seg_t create_data_seg(client_tcb_t* tcb,char* data,unsigned int length){
        seg_t seg;
        seg.header.src_port=tcb->client_portNum;
        seg.header.dest_port=tcb->server_portNum;
        seg.header.seq_num=tcb->next_seqNum;
        seg.header.ack_num=tcb->next_ackNum;
        seg.header.length=length;
        seg.header.type=DATA;
        memcpy(seg.data,data,length);
        return seg;
}

void fill_buffer(client_tcb_t* tcb,seg_t seg){
  segBuf_t* segBuf=(segBuf_t*)calloc(1,sizeof(segBuf_t));
  segBuf->sentTime=-1;
  segBuf->seg=seg;
  
  concurrent_printf_reminder("fill_buffer:seg:%d!\n",seg.header.seq_num);
  segBuf->next=NULL;
  if(tcb->sendBufTail==NULL){//缓冲区为空  
    tcb->sendBufTail=segBuf;
    tcb->sendBufHead->next=tcb->sendBufTail;
  }else {
    tcb->sendBufTail->next=segBuf;
    tcb->sendBufTail=segBuf;
  }
}

//一次性不会发送超过10个段
void send_buffer(client_tcb_t* tcb){
  if(tcb->unAck_segNum==GBN_WINDOW){
    concurrent_printf_reminder("GBN_Window is full!\n");
    tcb->wait_flag=1;
    pthread_cond_t* cond=&(cond_list[tcb->sockfd]);
    pthread_mutex_t* mutex=&(mutex_list[tcb->sockfd]);
    pthread_cond_wait(cond,mutex);
    tcb->wait_flag=0;
  }
  while(tcb->state==CONNECTED && tcb->sendBufunSent->next!=NULL && tcb->unAck_segNum<GBN_WINDOW){
      tcb->sendBufunSent=tcb->sendBufunSent->next;
      concurrent_printf_reminder("send_buffer:seg:%d,unAck_segNum:%d:\n",
                                  tcb->sendBufunSent->seg.header.seq_num,tcb->unAck_segNum);
      sip_sendseg(connection,tcb->server_nodeID,&tcb->sendBufunSent->seg);
      tcb->sendBufunSent->sentTime=(time_t)time(NULL);
      tcb->unAck_segNum++;
  }
}

int stcp_client_send(int sockfd, void* data, unsigned int length){
  int ret=0;
  pthread_mutex_t* mutex = &mutex_list[sockfd];
  client_tcb_t* tcb=NULL;
  do{
    pthread_mutex_lock(mutex);
    if((tcb=tcb_list[sockfd])!=NULL && tcb->state==CONNECTED){
      unsigned data_len = (length > MAX_SEG_LEN) ? MAX_SEG_LEN:length;
      seg_t seg=create_data_seg(tcb,data,data_len);
      tcb->next_seqNum += seg.header.length;
      fill_buffer(tcb,seg);
      send_buffer(tcb);
      data += seg.header.length;
      length -= seg.header.length;
    }else ret=-1;
    pthread_mutex_unlock(mutex);
    if(ret==-1)break;
  }while(length>0);

  return ret;
}

// 断开到STCP服务器的连接
//
// 这个函数用于断开到服务器的连接. 它以套接字ID作为输入参数. 套接字ID用于找到TCB表中的条目.  
// 这个函数发送FIN segment给服务器. 在发送FIN之后, state将转换到FINWAIT, 并启动一个定时器.
// 如果在最终超时之前state转换到CLOSED, 则表明FINACK已被成功接收. 否则, 如果在经过FIN_MAX_RETRY次尝试之后,
// state仍然为FINWAIT, state将转换到CLOSED, 并返回-1.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//

int stcp_client_disconnect(int sockfd) {
  client_tcb_t* tcb=NULL;
  pthread_mutex_t* mutex=&mutex_list[sockfd];
  seg_t* fin_seg=NULL;

  int server_id;
  pthread_mutex_lock(mutex);
  if((tcb=tcb_list[sockfd])!=NULL){
	server_id=tcb->server_nodeID;
    fin_seg=create_fin_seg(tcb->client_portNum,tcb->server_portNum,tcb->next_seqNum,tcb->next_ackNum);
    pthread_mutex_unlock(mutex);
  }else{
    pthread_mutex_unlock(mutex);
    return -1;
  }

  sip_sendseg(connection,server_id,fin_seg);

  pthread_mutex_lock(mutex);
  if((tcb=tcb_list[sockfd])!=NULL){
    tcb->state=FINWAIT;
    pthread_mutex_unlock(mutex);
  }else{
    pthread_mutex_unlock(mutex);
    return -1;
  }

  int is_closed=0;
  for(int i=0;i<FIN_MAX_RETRY;i++){
    clock_t start,end;
    double duration=0;
    start=clock();
    while(duration<FIN_TIMEOUT){
      pthread_mutex_lock(mutex);
      if((tcb=tcb_list[sockfd])!=NULL){
        if(tcb->state==CLOSED)is_closed=1;
        pthread_mutex_unlock(mutex);
      }else{
        pthread_mutex_unlock(mutex);
        return -1;
      }
      if(is_closed)break;
      end=clock();
      duration = ((double)(end-start)/CLOCKS_PER_SEC) * 1e9;
    }
    if(is_closed)
      break;
    else{
      concurrent_printf_reminder("stcp_client_disconnect:retry fin_seg!\n");
      sip_sendseg(connection,server_id,fin_seg);
    }
  }

  if(is_closed){
    concurrent_printf_reminder("stcp_client_disconnect successed!\n");
    return 0;
  }else {
    pthread_mutex_lock(mutex);
    if((tcb=tcb_list[sockfd])!=NULL){
      tcb->state=CLOSED;
    }
    pthread_mutex_unlock(mutex);
    return -1;
  }
}

// 关闭STCP客户
//
// 这个函数调用free()释放TCB条目. 它将该条目标记为NULL, 成功时(即位于正确的状态)返回1,
// 失败时(即位于错误的状态)返回-1.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//

int stcp_client_close(int sockfd) {
  pthread_mutex_t* mutex=&mutex_list[sockfd];
  client_tcb_t* tcb=NULL;
  int ret=-1;
  pthread_mutex_lock(mutex);
  if((tcb=tcb_list[sockfd])!=NULL){
    if(tcb->state==CLOSED){
      ret=1;
      concurrent_printf_reminder("stcp_client_close successed!\n");
    }else{
      tcb->state=CLOSED;
    }
    if(tcb->wait_flag){
      pthread_cond_t* cond=&(cond_list[tcb->sockfd]);
      pthread_cond_signal(cond);
    }
    free(tcb);
    tcb_list[sockfd]=NULL;
  }
  pthread_mutex_unlock(mutex);
	return ret;
}

// 处理进入段的线程
//
// 这是由stcp_client_init()启动的线程. 它处理所有来自服务器的进入段. 
// seghandler被设计为一个调用sip_recvseg()的无穷循环. 如果sip_recvseg()失败, 则说明重叠网络连接已关闭,
// 线程将终止. 根据STCP段到达时连接所处的状态, 可以采取不同的动作. 请查看客户端FSM以了解更多细节.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
void client_closed(client_tcb_t*tcb,seg_t*seg){

}

void client_synsent(client_tcb_t*tcb,seg_t*seg){
  if(verify_synack_seg(seg,tcb->server_portNum,tcb->client_portNum)){
    tcb->state=CONNECTED;
  }
}

int verify_dataack_seg(client_tcb_t*tcb,seg_t*seg){
  if(seg->header.src_port != tcb->server_portNum || 
     seg->header.dest_port != tcb->client_portNum ||
     seg->header.seq_num != tcb->next_ackNum ||
     seg->header.ack_num > tcb->next_seqNum){
    return 0;
  }else{
    return 1;
  }
}

void client_connected(client_tcb_t*tcb,seg_t*seg){
  switch(seg->header.type){
    case DATAACK:{
      if(verify_dataack_seg(tcb,seg)){
        if(tcb->sendBufHead==tcb->sendBufunSent){
          //concurrent_printf_warning("client_connected:tcb->sendBufHead==tcb->sendBufunSent\n");
          return ;
        }
        int flag=0;
        if(tcb->sendBufTail==tcb->sendBufunSent){
          flag=1;
        }
        segBuf_t* head_next=tcb->sendBufHead->next;
        segBuf_t* sendBufunSent_next=tcb->sendBufunSent->next;
        int ack_flag=0;
        while(head_next!=sendBufunSent_next){
          if(seg->header.ack_num <= head_next->seg.header.seq_num)
              break;
          else ack_flag=1;
          segBuf_t* temp=head_next;
          head_next=head_next->next;
          tcb->sendBufHead->next=head_next;

          tcb->unAck_segNum--;
          concurrent_printf_reminder("seg %d acked! unAck_segNum:%d\n",temp->seg.header.seq_num,tcb->unAck_segNum);
          free(temp);
        }
        if(head_next==sendBufunSent_next){
          tcb->sendBufunSent=tcb->sendBufHead;
          concurrent_printf("GBN_Window is empty!\n");
          if(flag)tcb->sendBufTail=NULL;
        }
        if(ack_flag && tcb->wait_flag){
          pthread_cond_t* cond=&(cond_list[tcb->sockfd]);
          pthread_cond_signal(cond);
        }
      }else{
        concurrent_printf_warning("verify_dataack_seg failed!\n");
      }
      break;
    }
    default:{
      concurrent_printf_warning("client_connected:client_connected:unknown seg type!\n");
    }
  }
}

void client_finawait(client_tcb_t*tcb,seg_t*seg){
  if(verify_finack_seg(seg,tcb->server_portNum,tcb->client_portNum,
                       tcb->next_ackNum,tcb->next_seqNum))
  {
    tcb->state=CLOSED;
  }
}

void handle_seg(int sockfd,seg_t* seg){
  pthread_mutex_t* mutex=&mutex_list[sockfd];
  pthread_mutex_lock(mutex);
  client_tcb_t* tcb=tcb_list[sockfd];
  if(tcb!=NULL){
    switch(tcb->state){
      case CLOSED:{
        client_closed(tcb,seg);
        break;
      }
      case SYNSENT:{
        client_synsent(tcb,seg);
        break;
      }
      case CONNECTED:{
        client_connected(tcb,seg);
        break;
      }
      case FINWAIT:{
        client_finawait(tcb,seg);
        break;
      }
      default:{
        concurrent_printf_warning("handle_seg:unknown client state!\n");
        break;
      }
    }
  }
  pthread_mutex_unlock(mutex);
}

int get_sockfd_by_port(unsigned int client_port){
	int sockfd=-1;
  
	for(int i=0;i<MAX_TRANSPORT_CONNECTIONS;i++){
		pthread_mutex_t *mutex=&mutex_list[i];
		pthread_mutex_lock(mutex);
		client_tcb_t* tcb=NULL;
		if((tcb=tcb_list[i])!=NULL && 
			tcb->client_portNum==client_port){
			  sockfd=i;
		  }
		pthread_mutex_unlock(mutex);
		if(sockfd!=-1)break;
	}
	return sockfd;
}

void* seghandler(void* arg) {
  pthread_t self_tid=pthread_self();
  pthread_detach(self_tid);

  while(1){
    seg_t* seg=(seg_t*)calloc(1,sizeof(seg_t));
	  int server_nodeID;
    int choice=sip_recvseg(connection,&server_nodeID,seg);
    if(choice==1){
      free(seg);
      continue;
    }
    if(choice==-1)break;
    
    int sockfd=get_sockfd_by_port(seg->header.dest_port);
    
    if(sockfd == -1){
			concurrent_printf_warning("client seghandler:target tcb can't be found!\n");
		}else{
			handle_seg(sockfd,seg);
		}
    free(seg);
  }
  return NULL;
}


// 这个线程持续轮询发送缓冲区以触发超时事件. 如果发送缓冲区非空, 它应一直运行.
// 如果(当前时间 - 第一个已发送但未被确认段的发送时间) > DATA_TIMEOUT, 就发生一次超时事件.
// 当超时事件发生时, 重新发送所有已发送但未被确认段. 当发送缓冲区为空时, 这个线程将终止.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
void* sendBuf_timer(void* args){
  u_int64_t sockfd=(u_int64_t)args;
  pthread_mutex_t* mutex=&mutex_list[sockfd];
  while(1){
    int exit_flag=0;
    pthread_mutex_lock(mutex);
    client_tcb_t* tcb=NULL;
    if((tcb=tcb_list[sockfd])==NULL || 
        tcb->state!=CONNECTED){
        exit_flag=1;
    }else if(tcb->sendBufunSent!=tcb->sendBufHead){
      segBuf_t* head_next=tcb->sendBufHead->next;
      if(head_next!=NULL){
        double duration=difftime(time(NULL),head_next->sentTime)*1e9;
        if(duration>DATA_TIMEOUT){
          while(tcb->unAck_segNum>0 && head_next!=tcb->sendBufunSent->next){
            sip_sendseg(connection,tcb->server_nodeID,&(head_next->seg));
            concurrent_printf_reminder("sendBuf_timer:retry data seg %d\n",head_next->seg.header.seq_num);
            head_next=head_next->next;
          }
        }
      }
    }
    pthread_mutex_unlock(mutex);
    if(exit_flag)break;
    usleep(SENDBUF_POLLING_INTERVAL/1000);
  }
  return NULL;
}